﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using Buddywing;
using Buddy.BehaviorTree;
using Buddy.Common;
using Buddy.CommonBot;
using Buddy.CommonBot.Settings;
using Buddy.Navigation;

using Action = Buddy.BehaviorTree.Action;

namespace WingIt.Routines
{
    [RevisionControlId("$Id: LazyRaider.cs 684 2012-08-08 10:09:07Z Chinajade $")]
    public class LazyRaider
    { //12Jul2012-14:27UTC chinajade
        public static void  BurnPhaseOff()      { if (IsBurnPhaseEnabled) { IsBurnPhaseEnabled = false; UpdateStateChange(); } }
        public static void  BurnPhaseOn()       { if (!IsBurnPhaseEnabled) { IsBurnPhaseEnabled = true; UpdateStateChange(); } }
        public static bool  BurnPhaseToggle()   { IsBurnPhaseEnabled = IsBurnPhaseEnabled ? false : true; UpdateStateChange(); return (IsBurnPhaseEnabled); }

        public static void  CombatOff()         { if (IsCombatEnabled) { IsCombatEnabled = false; UpdateStateChange(); } }
        public static void  CombatOn()          { if (!IsCombatEnabled) { IsCombatEnabled = true; UpdateStateChange(); } }
        public static bool  CombatToggle()      { IsCombatEnabled = IsCombatEnabled ? false : true; UpdateStateChange(); return (IsCombatEnabled); }

        public static void  MovementOff()       { if (IsMovementEnabled) { IsMovementEnabled = false; UpdateStateChange(); } }
        public static void  MovementOn()        { if (!IsMovementEnabled) { IsMovementEnabled = true; UpdateStateChange(); } }
        public static bool  MovementToggle()    { IsMovementEnabled = IsMovementEnabled ? false : true; UpdateStateChange(); return (IsMovementEnabled); }

        public static bool  IsBattlefieldManaged { get; private set; }
        public static bool  IsBurnPhaseEnabled  { get; private set; }
        public static bool  IsCombatEnabled     { get; private set; }
        public static bool  IsMovementEnabled   { get; private set; }

        public static string CurrentStateAsString()
        {
            return (string.Format("LazyRaider[Move({0}): {1},  Combat({2}): {3},  Burn({4}): {5}]",
                KeyName(Key_MovementToggle),
                (IsMovementEnabled ? "ENABLED" : "disabled"),
                KeyName(Key_CombatToggle),
                (IsCombatEnabled ? "ENABLED" : "disabled"),
                KeyName(Key_BurnPhaseToggle),
                (IsBurnPhaseEnabled ? "ENABLED" : "disabled")));
        }

        public static void  Initialize()
        {
            // NB: BuddyWing sometimes likes to initialize things more than once.
            // Be sure everything is sane here, if that happens.

            // Initialize the global context...
            IsBurnPhaseEnabled = false;
            IsCombatEnabled = true;
            IsMovementEnabled = true;
            IsMovementEnabled_Previous = IsMovementEnabled;
            IsBattlefieldManaged = IsMovementEnabled;
            PullDistanceCaptured = IsMovementEnabled ? ((float?)null) : CharacterSettings.Instance.PullDistance;

            // Hook the appropriate hotkeys...
            Hotkeys.RegisterHotkey("Toggle BurnPhase", () => { BurnPhaseToggle(); }, Key_BurnPhaseToggle);
            Hotkeys.RegisterHotkey("Toggle Combat", () => { CombatToggle(); }, Key_CombatToggle);
            Hotkeys.RegisterHotkey("Toggle Movement", () => { MovementToggle(); }, Key_MovementToggle);

            // Capture the Buddy-provided Movement- & Navigation-providers, and mount preferences...
            if (BuiltinMovementProvider == null)
                { BuiltinMovementProvider = Navigator.MovementProvider; }

            if (BuiltinNavigationProvider == null)
                { BuiltinNavigationProvider = Navigator.NavigationProvider; }

            if (Setting_AutomaticMount == null)
                { Setting_AutomaticMount = CharacterSettings.Instance.AutomaticMount; }

            if (Setting_MountName == null)
                { Setting_MountName = CharacterSettings.Instance.MountName; }

            // Hook into startup/shutdown cleanup...
            // ...is handled in WingIt.cs.
            //Buddywing.App.Current.Exit += new System.Windows.ExitEventHandler(OnAppExit);

            // Display 'help' and our initial state...
            Logger.Write("LazyRaider keybindings:\n" +
                "    MovementToggle: {0} (*all* movement--questing, combat, looting, and unstuck)\n" +
                "    CombatToggle: {1}\n" +
                "    BurnPhaseToggle: {2} (uses all available cooldowns in combat)",
                KeyName(Key_MovementToggle), KeyName(Key_CombatToggle), KeyName(Key_BurnPhaseToggle));
            Logger.Write(CurrentStateAsString());
        }

        #region Behavior Tree Conveniences
        public static Composite     CreateBTPS_NotifyAndShortCircuitWhenCombatDisabled()
        {
            return (new Decorator(whenCombatDisabled => !LazyRaider.IsCombatEnabled,
                new Action(logCombatDisabled => Logger.Write("Combat Disabled via Lazyraider ({0} to re-enable)", Key_CombatToggle))));

            // Note, if combat is disabled, RunStatus.Success is returned to the BT-parent.
            // This is what we desire for a behavior tree PrioritySelector parent.
        }
        #endregion

        #region Helpers
        private static string   KeyName(Keys    key)
        {
            string  keyName = key.ToString();

            // Repair Windoze nonsense...
            return ((keyName == "Next") ? "PageDown" : keyName);
        }

        private static void  OnAppExit(object sender, System.Windows.ExitEventArgs e)
        {
 	        ProcessHookUnhook(true);
        }

        private static void     OnBotStart(IBot bot)
        {
            ProcessHookUnhook(IsMovementEnabled);
            Logger.Write(CurrentStateAsString());
        }

        private static void     OnBotStop(IBot bot)
        {
            ProcessHookUnhook(true);
        }

        // NB: This hook may be called when the bot is running or when it is not. (E.g., the user can disable movement
        // before hitting the bot's "Start" button.)  Due to this situation, this method cannot make any assumptions
        // about the existing state; instead, state must be checked explicitly.
        private static void     ProcessHookUnhook(bool wantMovementEnabled)
        {
            // Install appropriate movement, navigation, & mount handlers...
            Navigator.MovementProvider = wantMovementEnabled ? BuiltinMovementProvider : NoMovementProvider;
            Navigator.NavigationProvider = wantMovementEnabled ? BuiltinNavigationProvider : NoNavigationProvider;

            if (CharacterSettings.Instance != null)
            {
                CharacterSettings.Instance.AutomaticMount = wantMovementEnabled ? Setting_AutomaticMount.Value : false;
                CharacterSettings.Instance.MountName = wantMovementEnabled ? Setting_MountName : string.Empty;

                if (wantMovementEnabled)
                {
                    if (PullDistanceCaptured.HasValue)
                    {
                        CharacterSettings.Instance.PullDistance = PullDistanceCaptured.Value;
                        PullDistanceCaptured = null;
                        Logger.Write("LazyRaider: Pull Distance restored to {0} meters", (CharacterSettings.Instance.PullDistance * 10));
                    }
                }
                else
                {
                    if (!PullDistanceCaptured.HasValue)
                        { PullDistanceCaptured = CharacterSettings.Instance.PullDistance; }
                    CharacterSettings.Instance.PullDistance = PullDistance_ForNoMovement;
                    Logger.Write("LazyRaider: Pull Distance set to {0} meters", (CharacterSettings.Instance.PullDistance * 10));
                }
            }
        }

        private static void     UpdateStateChange()
        {
            // If 'movement enable' changed state, we have some hooking/unhooking to do...
            if (IsMovementEnabled_Previous != IsMovementEnabled)
            {
                IsMovementEnabled_Previous = IsMovementEnabled;
                IsBattlefieldManaged = IsMovementEnabled;
                ProcessHookUnhook(IsMovementEnabled);
            }

            Logger.Write(CurrentStateAsString());
        }
        #endregion

        #region No Movement Replacements
        private class NoMovement_MovementProvider   : IMovementProvider
        {
            public void MoveTowards(Buddy.Common.Math.Vector3 location) { /*empty*/ }
            public void StopMovement() { /*empty*/ }
        };

        private class NoMovement_NavigationProvider : INavigationProvider
        {
            public void Clear() { /*empty*/ }
            public MoveResult MoveTo(Buddy.Common.Math.Vector3 destination, string destName, bool useRaycast, bool useStraightLine)
            {
                return (MoveResult.Moved);
            }
        };
        #endregion

        public static BotEvent                          BotEvent_OnBotStart = new BotEvent(OnBotStart);
        public static BotEvent                          BotEvent_OnBotStop  = new BotEvent(OnBotStop);
        private static IMovementProvider                BuiltinMovementProvider = null;
        private static INavigationProvider              BuiltinNavigationProvider = null;
        private static bool                             IsMovementEnabled_Previous { get; set; }
        private static Keys                             Key_BurnPhaseToggle = Keys.End;
        private static Keys                             Key_CombatToggle = Keys.PageUp;
        private static Keys                             Key_MovementToggle = Keys.PageDown;
        private static NoMovement_MovementProvider      NoMovementProvider = new NoMovement_MovementProvider();
        private static NoMovement_NavigationProvider    NoNavigationProvider = new NoMovement_NavigationProvider();
        private static float?                           PullDistanceCaptured = null;
        private const float                             PullDistance_ForNoMovement = 0.1f;
        private static bool?                            Setting_AutomaticMount = null;
        private static string                           Setting_MountName = null;
    }
}
